package com.ruoyi.web.controller.tool;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ChatServer {
    private final int port;
    private final ChannelGroup allChannels;

    public ChatServer(int port) {
        this.port = port;
        this.allChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    }

    public void start() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new StringEncoder(), new StringDecoder(), new ChatServerHandler(allChannels));
                        }
                    });

            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println("聊天室已启动，你使用的系统端口为： " + port);

            // Wait until the server socket is closed.
            future.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args.length > 0) {
            try {
                port = Integer.parseInt(args[0]);
            } catch (NumberFormatException e) {
                System.err.println("Invalid port number: " + args[0]);
                return;
            }
        }

        ChatServer server = new ChatServer(port);
        server.start();
    }
}

class ChatServerHandler extends ChannelInboundHandlerAdapter {
    private final ChannelGroup allChannels;
    private static final Map<Channel, String> nicknameMap = new ConcurrentHashMap<>();

    public ChatServerHandler(ChannelGroup allChannels) {
        this.allChannels = allChannels;
    }

    /**
     * 当通道变得活跃时（例如，连接被建立），这个方法会被调用。可以在这个方法中实现逻辑来初始化连接或发送初始消息
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("服务端：客户端连接地址 " + ctx.channel().remoteAddress());
        allChannels.add(ctx.channel());

        // 假设客户端在连接后发送的第一条消息是昵称
        //ctx.writeAndFlush("使用聊天室前，先输入您的昵称: ");
    }

    /**
     * 私聊
     * @param ctx
     * @param targetNickname
     * @param message
     */
    private void forwardPrivateMessage(ChannelHandlerContext ctx, String targetNickname, String message) {
        for (Channel channel : allChannels) {
            if (nicknameMap.get(channel).equals(targetNickname)) {
                StringBuilder output = new StringBuilder("[Private Message from ")
                        .append(nicknameMap.get(ctx.channel()))
                        .append("]: ")
                        .append(message)
                        .append("\r\n");
                channel.writeAndFlush(output);
                break;
            }
        }
    }

    /**
     * 当有数据被读取到通道中时，这个方法会被调用
     * @param ctx
     * @param objMsg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object objMsg) throws Exception {
        String msg = (String) objMsg;
        Channel channel = ctx.channel();
        if (nicknameMap.containsKey(channel)) {
            // 已设置昵称的用户发送消息
            if (msg.startsWith("/msg ")) {
                //私聊处理
                String[] parts = msg.split(" ", 3);
                if (parts.length == 3 && !parts[1].isEmpty()) {
                    String targetNickname = parts[1];
                    String privateMessage = parts[2];

                    // 查找并转发私聊消息
                    forwardPrivateMessage(ctx, targetNickname, privateMessage);

                    // 可选：向发送者反馈已发送私聊信息（根据实际需求决定是否需要）
                    ctx.writeAndFlush("Private message sent to " + targetNickname + ".\r\n");
                } else {
                    ctx.writeAndFlush("Invalid private message format. Please use '/msg [nickname] [message]'\r\n");
                }
            } else{ // 公共聊天或其他命令处理...
                broadcastMessage(nicknameMap.get(channel), msg);
            }
        } else if (msg.startsWith("/nick ")) {
            // 昵称设置处理
            String nickname = msg.substring(5).trim();
            if (!nickname.isEmpty()) {
                nicknameMap.put(ctx.channel(), nickname);
                broadcastMessage(nickname, "您已经加入聊天室.");
                // 设置完昵称后，提示用户可以开始聊天
                ctx.writeAndFlush("你的名字叫 '" + nickname + "'. 输入您的消息内容.\r\n");
            } else {
                ctx.writeAndFlush("Invalid nickname. Please enter a valid nickname.\r\n");
            }
        } else {
            ctx.writeAndFlush("You need to set a nickname first by typing '/nick <nickname>'.\r\n");
        }
    }

    /**
     * 当通道变得不活动时（例如，连接被关闭），这个方法会被调用。可以在这个方法中实现逻辑来清理资源
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        String nickname = nicknameMap.remove(ctx.channel());
        if (nickname != null) {
            broadcastMessage(nickname, "你已经离开聊天室.");
        }
        super.channelInactive(ctx);
    }

    private void broadcastMessage(String nickname, String message) {
        StringBuilder output = new StringBuilder(nickname).append(": ").append(message).append("\r\n");
        for (Channel channel : allChannels) {
            channel.writeAndFlush(output.toString());
        }
    }

    /**
     * 当处理入站事件发生异常时，这个方法会被调用。你可以在这里实现逻辑来处理异常。例如记录日志或关闭通道
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
